/**
 * JSON Schema 验证器
 *
 * 轻量级的 JSON Schema 验证实现
 * 支持 JSON Schema Draft 2020-12 的核心功能
 */

import type { JsonSchema } from './json-schema-adapter.js';
import { logger, createErrorLogContext } from '../utils/logger.js';

export interface ValidationResult {
  valid: boolean;
  errors?: string[];
  path?: string[];
}

export class JsonSchemaValidator {
  /**
   * 验证数据是否符合 JSON Schema
   */
  validate(data: any, schema: JsonSchema, path: string[] = []): ValidationResult {
    const errors: string[] = [];

    // 如果没有 schema，认为验证通过
    if (!schema || Object.keys(schema).length === 0) {
      return { valid: true };
    }

    // 类型验证
    if (schema.type) {
      const typeResult = this.validateType(data, schema.type, path);
      if (!typeResult.valid) {
        errors.push(...(typeResult.errors || []));
      }
    }

    // 根据类型进行具体验证
    const dataType = this.getType(data);

    switch (dataType) {
      case 'object':
        if (data !== null) {
          const objectResult = this.validateObject(data, schema, path);
          if (!objectResult.valid) {
            errors.push(...(objectResult.errors || []));
          }
        }
        break;

      case 'array':
        const arrayResult = this.validateArray(data, schema, path);
        if (!arrayResult.valid) {
          errors.push(...(arrayResult.errors || []));
        }
        break;

      case 'string':
        const stringResult = this.validateString(data, schema, path);
        if (!stringResult.valid) {
          errors.push(...(stringResult.errors || []));
        }
        break;

      case 'number':
        const numberResult = this.validateNumber(data, schema, path);
        if (!numberResult.valid) {
          errors.push(...(numberResult.errors || []));
        }
        break;
    }

    // 枚举验证
    if (schema.enum) {
      const enumResult = this.validateEnum(data, schema.enum, path);
      if (!enumResult.valid) {
        errors.push(...(enumResult.errors || []));
      }
    }

    // const 验证
    if (schema.const !== undefined) {
      if (!this.deepEqual(data, schema.const)) {
        errors.push(`${this.pathToString(path)}: Value must be ${JSON.stringify(schema.const)}`);
      }
    }

    return {
      valid: errors.length === 0,
      errors: errors.length > 0 ? errors : undefined,
      path,
    };
  }

  /**
   * 类型验证
   */
  private validateType(data: any, schemaType: string | string[], path: string[]): ValidationResult {
    const actualType = this.getType(data);
    const types = Array.isArray(schemaType) ? schemaType : [schemaType];

    if (!types.includes(actualType)) {
      return {
        valid: false,
        errors: [
          `${this.pathToString(path)}: Expected type ${types.join(' or ')}, got ${actualType}`,
        ],
      };
    }

    return { valid: true };
  }

  /**
   * 对象验证
   */
  private validateObject(data: any, schema: JsonSchema, path: string[]): ValidationResult {
    const errors: string[] = [];

    // properties 验证
    if (schema.properties) {
      for (const [key, propSchema] of Object.entries(schema.properties)) {
        if (key in data) {
          const result = this.validate(data[key], propSchema, [...path, key]);
          if (!result.valid) {
            errors.push(...(result.errors || []));
          }
        }
      }
    }

    // required 验证
    if (schema.required && Array.isArray(schema.required)) {
      for (const field of schema.required) {
        if (!(field in data)) {
          errors.push(`${this.pathToString([...path, field])}: Required field missing`);
        }
      }
    }

    // additionalProperties 验证
    if (schema.additionalProperties === false && schema.properties) {
      const allowedKeys = Object.keys(schema.properties);
      for (const key of Object.keys(data)) {
        if (!allowedKeys.includes(key)) {
          errors.push(`${this.pathToString([...path, key])}: Additional property not allowed`);
        }
      }
    } else if (typeof schema.additionalProperties === 'object' && schema.additionalProperties) {
      // 如果 additionalProperties 是一个 schema
      const definedKeys = schema.properties ? Object.keys(schema.properties) : [];
      for (const key of Object.keys(data)) {
        if (!definedKeys.includes(key)) {
          const result = this.validate(data[key], schema.additionalProperties, [...path, key]);
          if (!result.valid) {
            errors.push(...(result.errors || []));
          }
        }
      }
    }

    // minProperties 验证
    if (schema.minProperties !== undefined) {
      const keyCount = Object.keys(data).length;
      if (keyCount < schema.minProperties) {
        errors.push(
          `${this.pathToString(path)}: Object must have at least ${
            schema.minProperties
          } properties, got ${keyCount}`
        );
      }
    }

    // maxProperties 验证
    if (schema.maxProperties !== undefined) {
      const keyCount = Object.keys(data).length;
      if (keyCount > schema.maxProperties) {
        errors.push(
          `${this.pathToString(path)}: Object must have at most ${
            schema.maxProperties
          } properties, got ${keyCount}`
        );
      }
    }

    return {
      valid: errors.length === 0,
      errors: errors.length > 0 ? errors : undefined,
    };
  }

  /**
   * 数组验证
   */
  private validateArray(data: any[], schema: JsonSchema, path: string[]): ValidationResult {
    const errors: string[] = [];

    // items 验证
    if (schema.items) {
      if (typeof schema.items === 'object' && !Array.isArray(schema.items)) {
        // 所有项使用同一个 schema
        for (let i = 0; i < data.length; i++) {
          const result = this.validate(data[i], schema.items, [...path, String(i)]);
          if (!result.valid) {
            errors.push(...(result.errors || []));
          }
        }
      } else if (Array.isArray(schema.items)) {
        // 元组验证
        for (let i = 0; i < Math.min(data.length, schema.items.length); i++) {
          const result = this.validate(data[i], schema.items[i], [...path, String(i)]);
          if (!result.valid) {
            errors.push(...(result.errors || []));
          }
        }
      }
    }

    // minItems 验证
    if (schema.minItems !== undefined && data.length < schema.minItems) {
      errors.push(
        `${this.pathToString(path)}: Array must have at least ${schema.minItems} items, got ${
          data.length
        }`
      );
    }

    // maxItems 验证
    if (schema.maxItems !== undefined && data.length > schema.maxItems) {
      errors.push(
        `${this.pathToString(path)}: Array must have at most ${schema.maxItems} items, got ${
          data.length
        }`
      );
    }

    // uniqueItems 验证
    if (schema.uniqueItems) {
      const uniqueCheck = new Set();
      for (let i = 0; i < data.length; i++) {
        const serialized = JSON.stringify(data[i]);
        if (uniqueCheck.has(serialized)) {
          errors.push(`${this.pathToString([...path, String(i)])}: Duplicate item in array`);
        }
        uniqueCheck.add(serialized);
      }
    }

    return {
      valid: errors.length === 0,
      errors: errors.length > 0 ? errors : undefined,
    };
  }

  /**
   * 字符串验证
   */
  private validateString(data: string, schema: JsonSchema, path: string[]): ValidationResult {
    const errors: string[] = [];

    // minLength 验证
    if (schema.minLength !== undefined && data.length < schema.minLength) {
      errors.push(
        `${this.pathToString(path)}: String must be at least ${schema.minLength} characters, got ${
          data.length
        }`
      );
    }

    // maxLength 验证
    if (schema.maxLength !== undefined && data.length > schema.maxLength) {
      errors.push(
        `${this.pathToString(path)}: String must be at most ${schema.maxLength} characters, got ${
          data.length
        }`
      );
    }

    // pattern 验证
    if (schema.pattern) {
      try {
        const regex = new RegExp(schema.pattern);
        if (!regex.test(data)) {
          errors.push(
            `${this.pathToString(path)}: String does not match pattern ${schema.pattern}`
          );
        }
      } catch (e) {
        logger.warn('JSON Schema Validator: Invalid regex pattern', createErrorLogContext(e, {
          pattern: schema.pattern,
          path: this.pathToString(path)
        }));
        errors.push(`${this.pathToString(path)}: Invalid regex pattern ${schema.pattern}`);
      }
    }

    // format 验证（简化版）
    if (schema.format) {
      const formatResult = this.validateFormat(data, schema.format, path);
      if (!formatResult.valid) {
        errors.push(...(formatResult.errors || []));
      }
    }

    return {
      valid: errors.length === 0,
      errors: errors.length > 0 ? errors : undefined,
    };
  }

  /**
   * 数字验证
   */
  private validateNumber(data: number, schema: JsonSchema, path: string[]): ValidationResult {
    const errors: string[] = [];

    // minimum 验证
    if (schema.minimum !== undefined && data < schema.minimum) {
      errors.push(`${this.pathToString(path)}: Value must be >= ${schema.minimum}, got ${data}`);
    }

    // maximum 验证
    if (schema.maximum !== undefined && data > schema.maximum) {
      errors.push(`${this.pathToString(path)}: Value must be <= ${schema.maximum}, got ${data}`);
    }

    // exclusiveMinimum 验证
    if (schema.exclusiveMinimum !== undefined && data <= schema.exclusiveMinimum) {
      errors.push(
        `${this.pathToString(path)}: Value must be > ${schema.exclusiveMinimum}, got ${data}`
      );
    }

    // exclusiveMaximum 验证
    if (schema.exclusiveMaximum !== undefined && data >= schema.exclusiveMaximum) {
      errors.push(
        `${this.pathToString(path)}: Value must be < ${schema.exclusiveMaximum}, got ${data}`
      );
    }

    // multipleOf 验证
    if (schema.multipleOf !== undefined) {
      const division = data / schema.multipleOf;
      if (division !== Math.floor(division)) {
        errors.push(
          `${this.pathToString(path)}: Value must be a multiple of ${
            schema.multipleOf
          }, got ${data}`
        );
      }
    }

    return {
      valid: errors.length === 0,
      errors: errors.length > 0 ? errors : undefined,
    };
  }

  /**
   * 枚举验证
   */
  private validateEnum(data: any, enumValues: any[], path: string[]): ValidationResult {
    const errors: string[] = [];

    if (!enumValues.some(value => this.deepEqual(value, data))) {
      errors.push(
        `${this.pathToString(path)}: Value must be one of ${JSON.stringify(
          enumValues
        )}, got ${JSON.stringify(data)}`
      );
    }

    return {
      valid: errors.length === 0,
      errors: errors.length > 0 ? errors : undefined,
    };
  }

  /**
   * 格式验证（简化版）
   */
  private validateFormat(data: string, format: string, path: string[]): ValidationResult {
    const errors: string[] = [];

    switch (format) {
      case 'email':
        if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data)) {
          errors.push(`${this.pathToString(path)}: Invalid email format`);
        }
        break;

      case 'uri':
      case 'url':
        try {
          new URL(data);
        } catch (urlError) {
          logger.warn('JSON Schema Validator: URL format validation error', createErrorLogContext(urlError, {
            data: data,
            format: format,
            path: this.pathToString(path)
          }));
          errors.push(`${this.pathToString(path)}: Invalid URL format`);
        }
        break;

      case 'date':
        if (!/^\d{4}-\d{2}-\d{2}$/.test(data)) {
          errors.push(`${this.pathToString(path)}: Invalid date format (expected YYYY-MM-DD)`);
        }
        break;

      case 'time':
        if (!/^\d{2}:\d{2}:\d{2}$/.test(data)) {
          errors.push(`${this.pathToString(path)}: Invalid time format (expected HH:MM:SS)`);
        }
        break;

      case 'date-time':
        if (isNaN(Date.parse(data))) {
          errors.push(`${this.pathToString(path)}: Invalid date-time format`);
        }
        break;

      case 'uuid':
        if (
          !/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(data)
        ) {
          errors.push(`${this.pathToString(path)}: Invalid UUID format`);
        }
        break;

      // 其他格式可以根据需要添加
    }

    return {
      valid: errors.length === 0,
      errors: errors.length > 0 ? errors : undefined,
    };
  }

  /**
   * 获取数据类型
   */
  private getType(data: any): string {
    if (data === null) return 'null';
    if (Array.isArray(data)) return 'array';
    if (typeof data === 'number' && Number.isInteger(data)) {
      // 在 JSON Schema 中，integer 是 number 的子类型
      return 'number';
    }
    return typeof data;
  }

  /**
   * 深度比较
   */
  private deepEqual(a: any, b: any): boolean {
    if (a === b) return true;
    if (a === null || b === null) return false;
    if (typeof a !== typeof b) return false;

    if (typeof a === 'object') {
      if (Array.isArray(a) && Array.isArray(b)) {
        if (a.length !== b.length) return false;
        for (let i = 0; i < a.length; i++) {
          if (!this.deepEqual(a[i], b[i])) return false;
        }
        return true;
      }

      const keysA = Object.keys(a);
      const keysB = Object.keys(b);
      if (keysA.length !== keysB.length) return false;

      for (const key of keysA) {
        if (!keysB.includes(key)) return false;
        if (!this.deepEqual(a[key], b[key])) return false;
      }

      return true;
    }

    return false;
  }

  /**
   * 路径转字符串
   */
  private pathToString(path: string[]): string {
    if (path.length === 0) return 'root';
    return path.join('.');
  }
}
